package com.lytoyo.neihanduanzi.service.impl;

import cn.hutool.extra.pinyin.PinyinUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.lytoyo.neihanduanzi.mapper.BlogCommentLikedMapper;
import com.lytoyo.neihanduanzi.mapper.BlogMapper;
import com.lytoyo.neihanduanzi.mapper.UserAttentionMapper;
import com.lytoyo.neihanduanzi.mapper.UserMapper;
import com.lytoyo.neihanduanzi.model.Result;
import com.lytoyo.neihanduanzi.model.blog.BlogInfo;
import com.lytoyo.neihanduanzi.model.dto.BlogInfoDto;
import com.lytoyo.neihanduanzi.model.index.BlogIndex;
import com.lytoyo.neihanduanzi.model.index.SuggestIndex;
import com.lytoyo.neihanduanzi.model.user.UserAttention;
import com.lytoyo.neihanduanzi.model.user.UserInfo;
import com.lytoyo.neihanduanzi.model.vo.BlogInfoVo;
import com.lytoyo.neihanduanzi.repository.BlogIndexRepository;
import com.lytoyo.neihanduanzi.service.BlogService;
import com.lytoyo.neihanduanzi.service.UserService;
import com.lytoyo.neihanduanzi.utils.AuthContextHolder;
import com.lytoyo.neihanduanzi.utils.Constant;
import com.lytoyo.neihanduanzi.utils.WebsocketLocalContainerUtil;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.data.elasticsearch.core.suggest.Completion;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * Package:com.lytoyo.neihanduanzi.service.impl
 *
 * @ClassName:BlogServiceImpl
 * @Create:2025/3/3 12:16
 **/

@Service
@Slf4j
public class BlogServiceImpl extends ServiceImpl<BlogMapper, BlogInfo> implements BlogService {
    @Resource
    private BlogMapper blogMapper;

    @Resource
    private UserMapper userMapper;

    @Resource
    private ThreadPoolExecutor threadPoolExecutor;

    @Resource
    private UserService userService;

    @Resource
    private BlogIndexRepository blogIndexRepository;


    @Resource
    private RedisTemplate redisTemplate;

    @Resource
    private UserAttentionMapper userAttentionMapper;

    /**
     * 获取十条博客
     *
     * @return
     */
    @Override
    public Page<BlogInfoDto> getBlogList(Integer current, Integer size) {
        Page<BlogInfo> page = new Page<>(current, size);
        LambdaQueryWrapper<BlogInfo> queryWrapper = new LambdaQueryWrapper<BlogInfo>()
                .eq(BlogInfo::getBlogType, 1)
                .orderByDesc(BlogInfo::getCreateTime);

        Page<BlogInfo> blogPage = blogMapper.selectPage(page, queryWrapper);
        // 批量获取用户信息
        Set<Long> userIds = blogPage.getRecords().stream()
                .map(BlogInfo::getUserId)
                .collect(Collectors.toSet());
        if (!userIds.isEmpty()) {
            Map<Long, UserInfo> userMap = userMapper.selectBatchIds(userIds).stream()
                    .collect(Collectors.toMap(UserInfo::getId, Function.identity()));

            // 转换为DTO
            List<BlogInfoDto> dtoList = blogPage.getRecords().stream().map(blog -> {
                UserInfo user = userMap.get(blog.getUserId());
                BlogInfoDto dto = new BlogInfoDto();
                BeanUtils.copyProperties(blog, dto);
                if (user != null) {
                    dto.setNickname(user.getNickname())
                            .setHeadPhoto(user.getHeadPhoto());
                }
                return dto;
            }).collect(Collectors.toList());

            Page<BlogInfoDto> dtoPage = new Page<>();
            BeanUtils.copyProperties(blogPage, dtoPage, "records");
            dtoPage.setRecords(dtoList);

            return dtoPage;
        }

        return new Page<>();
    }

    /**
     * 获取十条图文博客
     * @param current
     * @param size
     * @return
     */
    @Override
    public Page<BlogInfoDto> getPictureBlogList(Integer current, Integer size) {
        Page<BlogInfo> page = new Page<>(current, size);
        LambdaQueryWrapper<BlogInfo> queryWrapper = new LambdaQueryWrapper<BlogInfo>()
                .eq(BlogInfo::getBlogType, 2)
                .orderByDesc(BlogInfo::getCreateTime);

        Page<BlogInfo> blogPage = blogMapper.selectPage(page, queryWrapper);

        // 转换为DTO
        Page<BlogInfoDto> dtoPage = new Page<>();
        BeanUtils.copyProperties(blogPage, dtoPage, "records");

        List<BlogInfoDto> dtoList = blogPage.getRecords().stream().map(blog -> {
            UserInfo user = userMapper.selectById(blog.getUserId());
            BlogInfoDto dto = new BlogInfoDto();
            BeanUtils.copyProperties(blog, dto);
            dto.setNickname(user.getNickname())
                    .setHeadPhoto(user.getHeadPhoto());
            return dto;
        }).collect(Collectors.toList());

        dtoPage.setRecords(dtoList);
        return dtoPage;
    }

    /**
     * 获取单条博客
     * @param blogId
     * @return
     */
    @Override
    public Result getSingleBlog(Long blogId) {
        BlogInfo blogInfo = this.getById(blogId);
        if(null == blogInfo){
            return Result.fail(-1,"博客已下架");
        }
        UserInfo userInfo = userMapper.selectById(blogInfo.getUserId());
        BlogInfoDto blogInfoDto = new BlogInfoDto();
        BeanUtils.copyProperties(blogInfo,blogInfoDto, BlogInfoDto.class);
        blogInfoDto.setHeadPhoto(userInfo.getHeadPhoto())
                   .setNickname(userInfo.getNickname());
        return Result.success(blogInfoDto);
    }


    @Override
    public Result saveBlog(Long userId, BlogInfo blogInfo) {
        //保存博客到数据库
        CompletableFuture<Void> saveBlogIndexCompletableFuture = CompletableFuture.runAsync(() -> {
            if (null != userId && blogInfo.getBlogType() != null && null != blogInfo.getFileName()) {
                String picture = blogInfo.getFileName().split(",")[0];
                blogInfo.setUserId(userId)
                        .setLiked(0L)
                        .setTimeOfPlay(0L)
                        .setComment(0L)
                        .setCollect(0L)
                        .setLiked(0L)
                        .setPicture(picture);
                this.save(blogInfo);
            }
        }, threadPoolExecutor);
        //上传博客信息到搜素引擎
        saveBlogIndexCompletableFuture.thenRun(()->{
            UserInfo userInfo = userService.getById(userId);
            BlogIndex blogIndex = new BlogIndex();
            BeanUtils.copyProperties(blogInfo,blogIndex, BlogIndex.class);
            blogIndex.setDate(new Date())
                    .setHeadPhoto(userInfo.getHeadPhoto())
                    .setNickname(userInfo.getNickname());
            blogIndexRepository.save(blogIndex);
        });
        //上传博客标题关键词到es
        saveBlogIndexCompletableFuture.thenRun(()->{
            String[] sentences = blogInfo.getTitle().split("[？！，。:]");
            sentences[0] = sentences[0].trim();
            //补全关键词
            SuggestIndex suggestIndex = new SuggestIndex();
            suggestIndex.setId(UUID.randomUUID().toString().replace("-",""))
                    .setTitle(sentences[0])
                    .setKeyword(new Completion(new String[]{sentences[0]}))
                    .setKeywordPinyin(new Completion(new String[]{PinyinUtil.getPinyin(sentences[0])}))
                    .setKeywordSequence(new Completion(new String[]{PinyinUtil.getFirstLetter(sentences[0], "")}));
        });
        //将博客信息推送给粉丝
        saveBlogIndexCompletableFuture.thenRun(()->{
            BlogInfoDto blogInfoDto = new BlogInfoDto();
            BeanUtils.copyProperties(blogInfo,blogInfoDto, BlogInfoDto.class);
            //当前在线用户
            Set<Long> onlineUserIds = WebsocketLocalContainerUtil.getAllUserId();
            //查询该用户的所有粉丝
            List<Long> userAttentionsUserIds = this.userAttentionMapper.selectObjs(new LambdaQueryWrapper<UserAttention>()
                    .eq(UserAttention::getUserId, userId)
                    .select(UserAttention::getFan));
            Set<Long> onlineUserFanIds = userAttentionsUserIds.stream().collect(Collectors.toSet());
            Set<Long> offLineUserFanIds = new HashSet<>(userAttentionsUserIds);
            //当前离线用户
            offLineUserFanIds.removeAll(onlineUserIds);
            //当前在线的粉丝
            onlineUserFanIds.removeAll(offLineUserFanIds);

            List<Object> list = new ArrayList<>();
            UserInfo userInfo = userService.getById(userId);
            blogInfoDto.setNickname(userInfo.getNickname()).setHeadPhoto(userInfo.getHeadPhoto());
            list.add(blogInfoDto);
            //为在线用户推送消息
            CompletableFuture<Void> pushCompletableFuture = CompletableFuture.runAsync(() -> {
                for (Long id : onlineUserFanIds) {
                    WebsocketLocalContainerUtil.sendFollowMsg(id, list);
                }
            }, threadPoolExecutor);
            //为离线用户存储消息
            CompletableFuture<Void> saveCompletableFuture = CompletableFuture.runAsync(() -> {
                for (Long id : offLineUserFanIds) {

                    redisTemplate.opsForList().leftPush(Constant.USER_FOLLOW_BLOG_ID_KEY + id, blogInfoDto.getId());
                    redisTemplate.expire(Constant.USER_FOLLOW_BLOG_ID_KEY + id, Constant.USER_FOLLOW_BLOG_ID_TTL, TimeUnit.DAYS);
                }
            }, threadPoolExecutor);
            CompletableFuture.allOf(saveCompletableFuture,pushCompletableFuture);
        });
        CompletableFuture.allOf(saveBlogIndexCompletableFuture);

        return Result.success(null);
    }



    /**
     * 用户给博客点赞
     *
     * @param blogId
     * @param userId
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public Result blogClickLiked(Long blogId, Long userId) {
        //查询是否有该点赞记录
        Integer count = blogMapper.selectIsLiked(blogId, userId);
        //已有点赞记录
        if (null != count && count > 0) {
            return Result.fail(-1, "已点过赞");
        }
        //查询是否曾经有过点赞,然后取消的记录
        count = blogMapper.selectEverIsLiked(blogId, userId);
        //存在被取消的情况
        if (null != count && count > 0) {
            //恢复该记录
            blogMapper.recoverLikedRecord(blogId, userId);
        } else {
            //存储用户点赞博客信息
            blogMapper.addBlogLiked(blogId, userId);
        }
        //增加博客点赞数量
        this.update(new LambdaUpdateWrapper<BlogInfo>().eq(BlogInfo::getId, blogId).setSql("liked = liked + 1"));
        return Result.success("点赞成功");
    }

    /**
     * 取消点赞博客
     *
     * @param blogId
     * @param userId
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public Result blogCacelLiked(Long blogId, Long userId) {
        //查询是否有该点赞记录
        Integer count = blogMapper.selectIsLiked(blogId, userId);
        if (null != count && count > 0) {
            //将该点赞记录取消
            blogMapper.cacelLiked(blogId, userId);
            //点赞次数减一
            this.update(new LambdaUpdateWrapper<BlogInfo>().eq(BlogInfo::getId, blogId).setSql("liked = liked - 1"));
            return Result.success("取消点赞");
        } else {
            return Result.fail(-1, "数据异常");
        }
    }

    /**
     * 获取用户对视频的点赞收藏情况
     *
     * @return
     */
    @Override
    public Map getUserForBlogStatus() {
        Long userId = AuthContextHolder.getUserId();
        HashMap<String, List<Long>> map = new HashMap<>();
        //获取用户的点赞情况
        List<Long> likedList = this.blogMapper.selectLikedState(userId);
        List<Long> collectList = this.blogMapper.selectCollectState(userId);
        List<Long> commentLikedList = this.blogMapper.selectCommentLikedState(userId);
        map.put("liked", likedList == null ? new ArrayList<>() : likedList);
        map.put("collect", collectList == null ? new ArrayList<>() : collectList);
        map.put("commentLiked",commentLikedList == null? new ArrayList<>() : commentLikedList);
        return map;
    }

    /**
     * 获取用户自己的作品
     * @param userId
     * @return
     */
    @Override
    public Result getMineProduction(Long userId) {
        List<BlogInfo> blogInfoList = blogMapper.selectList(new LambdaQueryWrapper<BlogInfo>().eq(BlogInfo::getUserId, userId));
        List<BlogInfoVo> blogInfoVos = new ArrayList<>();
        if(blogInfoList.size()>0){
            for(BlogInfo blog : blogInfoList){
                BlogInfoVo blogInfoVo = new BlogInfoVo();
                BeanUtils.copyProperties(blog,blogInfoVo);
                blogInfoVos.add(blogInfoVo);
            }
        }
        return Result.success(blogInfoVos);
    }

    /**
     * 查询用户收藏博客
     * @param userId
     * @param blogIdList
     * @return
     */
    @Override
    public Result getMineCollect(Long userId, List<Long> blogIdList) {
        List<BlogInfoVo> blogInfoVos = new ArrayList<>();
        if(blogIdList.size() > 0){
            List<BlogInfo> blogInfoList = this.blogMapper.selectBatchIds(blogIdList);
            for(BlogInfo blogInfo : blogInfoList){
                BlogInfoVo blogInfoVo = new BlogInfoVo();
                BeanUtils.copyProperties(blogInfo,blogInfoVo, BlogInfoVo.class);
                blogInfoVos.add(blogInfoVo);
            }
        }
        return Result.success(blogInfoVos);
    }

    /**
     * 查询用户喜欢
     * @param userId
     * @param blogIdList
     * @return
     */
    @Override
    public Result getMineLiked(Long userId, List<Long> blogIdList) {
        List<BlogInfoVo> blogInfoVos = new ArrayList<>();
        if(blogIdList.size()>0){
            List<BlogInfo> blogInfoList = this.blogMapper.selectBatchIds(blogIdList);
            for(BlogInfo blogInfo : blogInfoList){
                BlogInfoVo blogInfoVo = new BlogInfoVo();
                BeanUtils.copyProperties(blogInfo,blogInfoVo,BlogInfoVo.class);
                blogInfoVos.add(blogInfoVo);
            }
        }
        return Result.success(blogInfoVos);
    }
}
